#include "terramodel.h"
#include "../external/paramo/paramo.h"
#include <QFile>
#include <QDebug>

std::shared_ptr<TerraModel> terra_model = nullptr;

TerraModel::TerraModel()
{
    // 读取配置
    readConfig();

    // 分配地形数据资源
    _grids = std::make_shared<SkipGrid>(_min_index, _max_index);

    if (!_grids)
    {
        qDebug() << "地形数据指针创建失败。";
    }
}

void TerraModel::toVector(std::vector<Tile> &vec)
{
    vec.clear();

    // 获取所有索引点
    std::vector<GridNode<int, unsigned> > idxs;
    {
        // 锁定访问
        std::unique_lock<std::mutex> lock(_grids_mtx);
        _grids->toVector(idxs);
    }


    // 预分配空间
    vec.reserve(idxs.size());

    // 索引值转坐标值
    for (size_t i = 0; i < idxs.size(); ++i)
    {
        struct Tile tile;
        indexToCoordinate(idxs[i].ix, idxs[i].iy, tile.x, tile.y);
        tile.id = idxs[i].val;
        vec.emplace_back(tile);
    }
}

void TerraModel::toVector(std::vector<Tile> &vec, double x_min, double x_max, double y_min, double y_max)
{
    vec.clear();

    // 合法范围判定
    if (x_min > x_max || y_min > y_max) return;

    // 计算坐标范围对应的索引范围
    int ix_min, ix_max, iy_min, iy_max;
    coordinateToIndex(x_min, y_min, ix_min, iy_min);
    coordinateToIndex(x_max, y_max, ix_max, iy_max);

    // 获取有界索引点
    std::vector<GridNode<int, unsigned> > idxs;
    {
        // 锁定访问
        std::unique_lock<std::mutex> lock(_grids_mtx);
        _grids->toVector(idxs, ix_min, ix_max, iy_min, iy_max);
    }


    // 预分配空间
    vec.reserve(idxs.size());

    // 索引值转坐标值
    for (size_t i = 0; i < idxs.size(); ++i)
    {
        struct Tile tile;
        indexToCoordinate(idxs[i].ix, idxs[i].iy, tile.x, tile.y);
        tile.id = idxs[i].val;
        vec.emplace_back(tile);
    }
}

void TerraModel::loadTerra(const QString &path)
{
    // 载入文件
    QFile file(path);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "地形文件无法打开";
        return;
    }

    // 解析文件
    QTextStream in(&file);

    // 首行校验
    QString first_line = in.readLine();
    if (first_line.trimmed() != "$TERRA$")
    {
        qDebug() << "文件格式错误";
        return;
    }

    // 锁定访问
    std::unique_lock<std::mutex> lock(_grids_mtx);

    // 逐行解析
    while (!in.atEnd())
    {
        QString line = in.readLine();
        QStringList coords = line.split(',', Qt::SkipEmptyParts);

        if (coords.size() != 3)
        {
            qDebug() << "数据行错误";
            continue;
        }

        _grids->integrate(coords[0].toInt(), coords[1].toInt(), coords[2].toUInt());
    }

    qDebug() << "地形载入成功";
}

// 手动触发或CTRL+S触发
void TerraModel::saveTerra(const QString &path)
{
    // 载入文件
    QFile file(path);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "地形文件无法打开";
        return;
    }

    // 解析文件
    QTextStream out(&file);

    // 写入校验
    out << "$TERRA$\n";

    // 逐行写入
    std::vector<typename SkipGrid::Grid> grids;
    {
        std::unique_lock<std::mutex> lock(_grids_mtx);
        _grids->toVector(grids);
    }

    for (size_t i = 0; i < grids.size(); ++i)
    {
        out << grids[i].ix << "," << grids[i].iy << "," << grids[i].val << "\n";
    }

    qDebug() << "地形保存成功";
}

void TerraModel::readConfig()
{
    bool flag = true;
    double resolution;
    int min_index, max_index;

    // 尝试读取配置
    if (!PARAMO->get("terra.resolution", resolution)) flag = false;
    if (!PARAMO->get("terra.minIndex", min_index)) flag = false;
    if (!PARAMO->get("terra.maxIndex", max_index)) flag = false;

    // 配置有效则设置
    if (flag)
    {
        _resolution = resolution;
        _min_index = min_index;
        _max_index = max_index;
    }
    else
    {
        // 配置无效写入默认值
        PARAMO->put("terra.resolution", _resolution);
        PARAMO->put("terra.minIndex", _min_index);
        PARAMO->put("terra.maxIndex", _max_index);
    }

    // 读取地形表数据
    int num;
    if (PARAMO->get("materials.num", num))
    {
        for (int i = 0; i < num; i++)
        {
            // 重置标志
            flag = true;

            std::string comment;    // 注释
            std::string name;       // 名称
            int coef;           // 系数
            int r, g, b, a;     // 颜色

            // 尝试读取数据
            if (!PARAMO->get("materials." + std::to_string(i) + "._comment", comment)) flag = false;
            if (!PARAMO->get("materials." + std::to_string(i) + ".name", name)) flag = false;
            if (!PARAMO->get("materials." + std::to_string(i) + ".coef", coef)) flag = false;
            if (!PARAMO->get("materials." + std::to_string(i) + ".R", r)) flag = false;
            if (!PARAMO->get("materials." + std::to_string(i) + ".G", g)) flag = false;
            if (!PARAMO->get("materials." + std::to_string(i) + ".B", b)) flag = false;
            if (!PARAMO->get("materials." + std::to_string(i) + ".A", a)) flag = false;

            // 如何数据有效创建材料
            if (flag)
            {
                _material_table[i] = Material{
                    QString::fromStdString(comment),
                    QString::fromStdString(name),
                    coef,
                    QColor(r, g, b, a)
                };
            }
        }
    }
}
